#include <stdint.h>
#include "mb.h"
#include "mbutils.h"
#include "modbus_slave_mid.h"
#include <stdbool.h>
#include <stdio.h>

#define ModbusMode   MB_RTU  // modebus模式 RTU模式
#define REG_INSTANCE_ARR_MAX 10 //寄存器实例配置的最大值
static MbRegisterInstance_t g_regInstanceArr[REG_INSTANCE_ARR_MAX]; //寄存器实例配置列表
static uint32_t regInstanceArrCurNum = 0; // 列表当前数量

/**
 * @desc  : 寄存器实例注册
 * @date  : 2025-04-15
 * @author: AnRui
 * @param : property - {参数说明}:
 * @param : address - {参数说明}:
 * @param : minValue - {参数说明}:
 * @param : maxValue - {参数说明}:
 * @param : ReadCb - {参数说明}:
 * @param : WriteCb - {参数说明}:
 * @return: bool
 * @note  :
 */
bool RegInstanceArrReg(MbRegisterInstance_t mbRegisterInstance) {
    // 寄存器实例数量检查,
    if (regInstanceArrCurNum >= REG_INSTANCE_ARR_MAX) return false;
    g_regInstanceArr[regInstanceArrCurNum++] = mbRegisterInstance;
    return true;
}


/**
 * @desc  : 读模式回调函数
 * @date  : 2025-04-15
 * @author: AnRui
 * @param : startAddr - {参数说明}: 寄存器地址起始地址
 * @param : regNum - {参数说明}:
 * @param : buf - {参数说明}: 协议栈缓冲区中的数据地址指针
 * @return: eMBErrorCode
 * @note  :
 */
static eMBErrorCode ReadRegsCb(uint8_t startAddr, uint8_t regNum, uint8_t* buf) {
    // 检测数据区域首字节指针是否为空指针 或者从机寄存器实例列表为空
    if ((buf == NULL) || (regInstanceArrCurNum == 0)) {
        return MB_EINVAL;
    }
    /* 遍历请求的所有寄存器 */
    for (uint32_t i = 0; i < regNum; i++) {
        MbRegisterInstance_t* instance = NULL;
			  /* 根据寄存器地址+偏移 在注册表中查找目标寄存器 */
        for (uint32_t j = 0; j < regInstanceArrCurNum; j++) {
            // 本次读取的地址 等于传入的寄存器地址+相对偏移
            if (g_regInstanceArr[j].address != startAddr + i) {
                continue;
            }
            // 读权限检查
            if ((g_regInstanceArr[j].property & MB_R) == 0) {
                continue;
            }
            instance = &g_regInstanceArr[j];
            if (instance->ReadCb != NULL) {
                // 每个寄存器的数据空间是两个uint_8  分为数据高位和低位
                // buf[2 * i] 找个该寄存器的初始地址   &buf[2 * i] 转成指针 在转成uin16_t指针
                // 最终的目的是在这个地址上填入数据
                instance->ReadCb((uint16_t*)&buf[2 * i]);
                // 0x0201  内存中以单字节储存
                // ARM 是小端模式 0x01 0x02
                // Modbus要求是大端模式 0x02 0x01
                // 所以要把小端模式转成大端模式
                uint8_t tmp = buf[2 * i];
                buf[2 * i] = buf[2 * i + 1];
                buf[2 * i + 1] = tmp;
            }
        }
        if (instance == NULL) {
            // 寄存器地址不合法
            return MB_ENOREG;
        }

    }

    return MB_ENOERR;
}


/**
 * @desc  : 写模式回调函数
 * @date  : 2025-04-15
 * @author: AnRui
 * @param : startAddr - {参数说明}:
 * @param : regNum - {参数说明}:
 * @param : buf - {参数说明}: 协议栈缓冲区中的数据区域首字节指针
 * @return: eMBErrorCode
 * @note  :
 */
static eMBErrorCode WriteRegsCb(uint8_t startAddr, uint8_t regNum, uint8_t* buf) {
    // 检测数据区域首字节指针是否为空指针 或者从机寄存器实例列表为空
    if ((buf == NULL) || (regInstanceArrCurNum == 0)) {
        return MB_EINVAL;
    }
    for (uint32_t i = 0; i < regNum; i++) {

        MbRegisterInstance_t* instance = NULL;
        for (uint32_t j = 0; j < regInstanceArrCurNum; j++) {
            if (g_regInstanceArr[j].address != startAddr + i) {
                continue;
            }
            //写权限检验
            if ((g_regInstanceArr[j].property & MB_W) == 0) {
                continue;
            }
            instance = &g_regInstanceArr[j];
            // 取出该寄存器的数据  高位|低位
            uint16_t setValue = ((buf[2 * i] << 8) & 0xFF00) | (buf[2 * i + 1] & 0xFF);
            if ((setValue < instance->minValue) || (setValue > instance->maxValue)) {
                return MB_EINVAL;
            }

            if (instance->WriteCb != NULL) {
                instance->WriteCb(setValue);
            }
        }

        if (instance == NULL) {
            return MB_ENOREG;
        }
    }
    return MB_ENOERR;
}



/**
 * @brief  保持寄存器回调函数（必须由用户实现）
 * @desc   处理Modbus功能码03/06/16对应的保持寄存器读写操作
 *         03(读保持寄存器) -> 触发MB_REG_READ模式
 *         06(写单个寄存器)/16(写多个寄存器) -> 触发MB_REG_WRITE模式
 * 
 * @param  pucRegBuffer : [OUT]读模式时填充数据的缓冲区 | [IN]写模式时待写入的数据
 * @param  usAddress    : 寄存器起始地址(0~65535)
 * @param  usNRegs      : 要操作的寄存器数量
 * @param  eMode        : 操作模式 
 *                       MB_REG_READ  - 读取寄存器值到pucRegBuffer
 *                       MB_REG_WRITE - 将pucRegBuffer数据写入寄存器
 * 
 * @return eMBErrorCode : 错误代码
 *         MB_ENOERR    - 操作成功
 *         MB_ENOREG    - 寄存器地址无效
 *         MB_ETIMEDOUT - 操作超时等
 * 
 * @note 1. 需自行校验usAddress和usNRegs的合法性
 *       2. 写模式时需处理字节序转换(大端模式)
 *       3. 缓冲区大小至少为usNRegs*2字节
 */

eMBErrorCode eMBRegHoldingCB(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode) {
    printf("eMBRegHoldingCB trigger\n");
    eMBErrorCode state;
    if (eMode == MB_REG_READ) {
        state = ReadRegsCb(usAddress, usNRegs, pucRegBuffer);
    } else {
        state = WriteRegsCb(usAddress, usNRegs, pucRegBuffer);
    }
    return state;
}


/**
 * @desc  : modbus从机初始化
 * @date  : 2025-04-15
 * @author: AnRui
 * @param : mbInstance - {参数说明}:
 * @note  : 传入结构体 ModbusSlaveInstance_t 里面定义了 读写功能码不同的处理函数
 */
void ModbusSlaveInit(ModbusSlaveInstance_t* mbInstance) {
    // RTU模式 从机地址 端口号:默认为0 波特率 校验位设置:MB_PAR_NONE 无校验 跟usart的设置有关
    eMBErrorCode res=eMBInit(ModbusMode, mbInstance->slaveAddr, 0, mbInstance->baudRate, MB_PAR_NONE);
    if(res!=MB_ENOERR) {
        printf("modbus init error\n");
    }
    // 激活协议栈
    eMBEnable();
}


/**
 * @desc  : Modbus轮询调度
 * @date  : 2025-04-15
 * @author: AnRui
 * @note  :
 */
void ModbusSlavePoll(void) {
    eMBPoll();
}



void __aeabi_assert(const char* expr, const char* file, int line) {
    // 输出信息（如通过ITM或UART）
    printf("ARM Assert: %s, %s:%d\n", expr, file, line);
    // 终止程序
    while (1);
}
